/*
===========================================================================
This source file is part of DyLab (Dynamic Laboratory)
For the latest info, see http://dylab.googlecode.com

Copyright (c) 2006-2008 Lukas Krejci
(krejci.lukas@volny.cz)

This file is part of DyLab.

    DyLab is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    DyLab is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with DyLab.  If not, see <http://www.gnu.org/licenses/>.
===========================================================================
*/

#ifndef __dylab_Matrix3x3_h__
#define __dylab_Matrix3x3_h__

//===========================================================================================
#include <dylab/dylab.h>
#include <dylab/utils/EnumIterator.h>
#include <dylab/utils/Array.h>
#include <dylab/utils/math/Math.h>
#include <dylab/utils/geometry/Vector3.h>

//===========================================================================================
namespace dylab {

    /** The square matrix with 3 x 3 elements.
    @remarks
		Matrix elements are stored in one array (3 rows).
    */
    class DYLAB_EXPORT Matrix3x3 
    {
    public:
        /// The indices of matrix elements
        enum elementidx_t
        {
            /* // */ E_00 = 0,	E_01,		E_02, /* \\ */
            /* || */ E_10,		E_11,		E_12, /* || */
			/* \\ */ E_20,		E_21,		E_22  /* // */
        };
        ///
        class DYLAB_EXPORT ElementIdxIterator : public EnumIterator<elementidx_t, E_00, E_22> { };
        ///
        enum { E_COUNT = E_22 + 1 };
    public:
        /// Matrix containing all zeroes.
        static const Matrix3x3 ZERO;
		/// Matrix with ones on diagonal and zeros everywhere else.
        static const Matrix3x3 IDENTITY;
    private:
        /// Array containing the elements of this matrix.
        union 
        {
            struct { real_t mE00, mE01, mE02, mE10, mE11, mE12, mE20, mE21, mE22; };
			real_t mElements[E_COUNT];
		};
    public:

        //-----------------------------------------------------------------------------
        // constructors
        //-----------------------------------------------------------------------------

        /**          
         */
        Matrix3x3() 
                { }

        /**
        */
        Matrix3x3(real_t _e00, real_t _e01, real_t _e02, 
				  real_t _e10, real_t _e11, real_t _e12,
				  real_t _e20, real_t _e21, real_t _e22) 
            : mE00(_e00), mE01(_e01), mE02(_e02), 
			  mE10(_e10), mE11(_e11), mE12(_e12), 
			  mE20(_e20), mE21(_e21), mE22(_e22)
                { }

        /**
        */
        Matrix3x3(const Matrix3x3 & _x) 
            : mE00(_x.mE00), mE01(_x.mE01), mE02(_x.mE02), 
			  mE10(_x.mE10), mE11(_x.mE11), mE12(_x.mE12), 
			  mE20(_x.mE20), mE21(_x.mE21), mE22(_x.mE22)
                { }

        Matrix3x3(const ArrayReal<9> & _x)
            : mE00(_x[E_00]), mE01(_x[E_01]), mE02(_x[E_02]), 
			  mE10(_x[E_10]), mE11(_x[E_11]), mE12(_x[E_12]), 
			  mE20(_x[E_20]), mE21(_x[E_21]), mE22(_x[E_22])
                { } 

        //-----------------------------------------------------------------------------
        // attribut access
        //-----------------------------------------------------------------------------

        /**
        */
        real_t & operator[] (elementidx_t _elementIdx) 
                { return * (&mE00 + _elementIdx); }

        /**
        */
        const real_t & operator[] (elementidx_t _elementIdx) const
                { return * (&mE00 + _elementIdx); }

        /**
        */
        real_t & operator[] (uint32_t _elementIdx) 
                { 
                    DYLAB_ASSERT(_elementIdx < E_COUNT);
                    return * (&mE00 + _elementIdx); 
                }

        /**
        */
        const real_t & operator[] (uint32_t _elementIdx) const
                { 
                    DYLAB_ASSERT(_elementIdx < E_COUNT);
                    return * (&mE00 + _elementIdx); 
                }

        //-----------------------------------------------------------------------------
        // properties
        //-----------------------------------------------------------------------------

        //-----------------------------------------------------------------------------
        // manipulators
        //-----------------------------------------------------------------------------

        /**
        */
        Matrix3x3 & add(const Matrix3x3 & _x) 
                { 
					mE00 += _x.mE00, mE01 += _x.mE01, mE02 += _x.mE02,
					mE10 += _x.mE10, mE11 += _x.mE11, mE12 += _x.mE12,
					mE20 += _x.mE20, mE21 += _x.mE21, mE22 += _x.mE22;
					return *this; 
				}

        /**
        */
        Matrix3x3 & add(real_t _r) 
                { return add(Matrix3x3(_r, _r, _r, _r, _r, _r, _r, _r, _r)); }
        
        /**        
         */
        Matrix3x3 & sub(const Matrix3x3 & _x) 
                { 
					mE00 -= _x.mE00, mE01 -= _x.mE01, mE02 -= _x.mE02,
					mE10 -= _x.mE10, mE11 -= _x.mE11, mE12 -= _x.mE12,
					mE20 -= _x.mE20, mE21 -= _x.mE21, mE22 -= _x.mE22;
					return *this; 
				}

        /**        
         */
        Matrix3x3 & sub(real_t _r) 
                { return sub(Matrix3x3(_r, _r, _r, _r, _r, _r, _r, _r, _r)); }

        /**
        */
        Matrix3x3 & mul(real_t _x) 
                { 
					mE00 *= _x, mE01 *= _x, mE02 *= _x,
					mE10 *= _x, mE11 *= _x, mE12 *= _x,
					mE20 *= _x, mE21 *= _x, mE22 *= _x;
					return *this; 
				}

        /**
        */
        Matrix3x3 & mul(const Matrix3x3 & _x)				
				{
					//TODO
					return *this;
				}

        /**
        */
        Vector3 mul(const Vector3 & _v) const
                { 
					return Vector3(mE00 * _v.mX + mE01 * _v.mY + mE02 * _v.mZ,
								   mE10 * _v.mX + mE11 * _v.mY + mE12 * _v.mZ,
								   mE20 * _v.mX + mE21 * _v.mY + mE22 * _v.mZ);
				}

        /**        
         */
        Matrix3x3 & div(real_t _x) 
                { 
					mE00 /= _x, mE01 /= _x, mE02 /= _x,
					mE10 /= _x, mE11 /= _x, mE12 /= _x,
					mE20 /= _x, mE21 /= _x, mE22 /= _x;
					return *this; 
				}

        /**
        */
        real_t computeDeterminant() const
				{ return (mE00*mE11*mE22)-(mE00*mE12*mE21)-(mE01*mE10*mE22)
						+(mE02*mE10*mE21)+(mE01*mE12*mE20)-(mE02*mE11*mE20); }

        /**
        */
		void buildFromAxisAndAngle(const Vector3 & _axis, const real_t _radians);

        //-----------------------------------------------------------------------------
        // operators
        //-----------------------------------------------------------------------------

        /**
        */
        Matrix3x3 operator + (const Matrix3x3 & _x) const
                { return Matrix3x3(*this).add(_x); }

        /**
        */
        Matrix3x3 operator - (const Matrix3x3 & _x) const
                { return Matrix3x3(*this).sub(_x); }


        /**
        */
        Matrix3x3 operator * (real_t _r) const
                { return Matrix3x3(*this).mul(_r); }

        /**
        */
        Matrix3x3 operator * (const Matrix3x3 & _x) const
                { return Matrix3x3(*this).mul(_x); }

        /**
        */
        Vector3 operator * (const Vector3 & _v) const
                { return mul(_v); }

        /**
        */
        Matrix3x3 operator / (real_t _r) const
                { return Matrix3x3(*this).div(_r); }

        /**
        */
        Matrix3x3 & operator = (const Matrix3x3 & _x) 
                { 
					mE00 = _x.mE00, mE01 = _x.mE01, mE02 = _x.mE02,
					mE10 = _x.mE10, mE11 = _x.mE11, mE12 = _x.mE12,
					mE20 = _x.mE20, mE21 = _x.mE21, mE22 = _x.mE22;
					return *this; 
				}

        /**
        */
        Matrix3x3 & operator += (const Matrix3x3 & _x) 
                { return add(_x); }

        /**
        */
        Matrix3x3 & operator += (real_t _r) 
                { return add(_r); }

        /**
        */
        Matrix3x3 & operator -= (const Matrix3x3 & _x) 
                { return sub(_x); }

        /**
        */
        Matrix3x3 & operator -= (real_t _r) 
                { return sub(_r); }

        /**
        */
        Matrix3x3 & operator *= (real_t _r) 
                { return mul(_r); }

        /**
        */
        Matrix3x3 & operator *= (const Matrix3x3 & _x) 
                { return mul(_x); }

        /**
        */
        Matrix3x3 & operator /= (real_t _r) 
                { return div(_r); }

        /**
        */
        bool operator == (const Matrix3x3 & _x) const
                { 
					return (mE00 == _x.mE00) && (mE01 == _x.mE01) && (mE02 == _x.mE02),
						   (mE10 == _x.mE10) && (mE11 == _x.mE11) && (mE12 == _x.mE12),
						   (mE20 == _x.mE20) && (mE21 == _x.mE21) && (mE22 == _x.mE22); 
				}

        /**
        */
        bool operator != (const Matrix3x3 & _x) const
                { return !operator==(_x); }

        //-----------------------------------------------------------------------------
        // conversions
        //-----------------------------------------------------------------------------

        operator String() const
				{ return String::fmt("[%1% %2% %3%]", Vector3(mE00, mE01, mE02), 
					Vector3(mE10, mE11, mE12), Vector3(mE20, mE21, mE22)); }
    };

    //===========================================================================================    
    inline std::ostream & operator << (std::ostream & _os, const Matrix3x3 & _o)
            { return _os << String(_o); }
}

//===========================================================================================
#endif // __dylab_Matrix3x3_h__
